﻿using System;
using System.Net;
using System.Linq.Expressions;
using System.Text;

namespace SL8.SL.Data.Helpers
{
    public class ConditionBuilder : ExpressionVisitor
    {
        StringBuilder sqlSB;
        public string Translate(Expression expression)
        {
            this.sqlSB = new StringBuilder();
            this.Visit(expression);
            return this.sqlSB.ToString();
        }

        private static Expression StripQuotes(Expression e)
        {
            while (e.NodeType == ExpressionType.Quote)
            {
                e = ((UnaryExpression)e).Operand;
            }

            return e;
        }

        protected override Expression VisitMethodCall(MethodCallExpression m)
        {
            if (m.Method.Name == "Contains")
            {
                sqlSB.Append(string.Format(" {0} LIKE '%{1}%' ", (((System.Linq.Expressions.MemberExpression)(m.Object)).Member).Name,
                    ((System.Linq.Expressions.ConstantExpression)(m.Arguments[0])).Value));

            }
            else
            {
                LambdaExpression lambda = (LambdaExpression)StripQuotes(m.Arguments[1]);
                this.Visit(lambda.Body);
            }

            return m;
        }


        protected override Expression VisitUnary(UnaryExpression u)
        {
            //throw new NotSupportedException("暂时不支持单目运算");
            switch (u.NodeType)
            {
                //case ExpressionType.Not:
                //    sb.Append(" NOT ");
                //    this.Visit(u.Operand);
                //    break;
                case ExpressionType.Convert:
                    this.Visit(u.Operand);
                    break;
                default:
                    throw new NotSupportedException(string.Format("The unary operator '{0}' is not supported", u.NodeType));
            }

            return u;
        }

        private bool IsNullConstantExpression(Expression expr)
        {
            ConstantExpression cExpr = expr as ConstantExpression;

            if (cExpr == null)
            {
                return false;
            }

            if (cExpr.Value == null)
            {
                return true;
            }

            return false;
        }

        protected override Expression VisitBinary(BinaryExpression b)
        {
            sqlSB.Append("(");
            this.Visit(b.Left);

            // 是否是空值
            if (this.IsNullConstantExpression(b.Right))
            {
                switch (b.NodeType)
                {
                    case ExpressionType.Equal:
                        sqlSB.Append(" IS NULL ");
                        break;
                    default:
                        sqlSB.Append(" IS NOT NULL ");
                        break;
                }
            }
            else
            {
                switch (b.NodeType)
                {
                    case ExpressionType.And:
                    case ExpressionType.AndAlso:
                        sqlSB.Append(" AND ");
                        break;
                    case ExpressionType.Or:
                    case ExpressionType.OrElse:
                        sqlSB.Append(" OR ");
                        break;
                    case ExpressionType.Equal:
                        sqlSB.Append(" = ");
                        break;
                    case ExpressionType.NotEqual:
                        sqlSB.Append(" <> ");
                        break;
                    case ExpressionType.LessThan:
                        sqlSB.Append(" < ");
                        break;
                    case ExpressionType.LessThanOrEqual:
                        sqlSB.Append(" <= ");
                        break;
                    case ExpressionType.GreaterThan:
                        sqlSB.Append(" > ");
                        break;
                    case ExpressionType.GreaterThanOrEqual:
                        sqlSB.Append(" >= ");
                        break;
                    default:
                        throw new NotSupportedException(string.Format("The binary operator '{0}' is not supported", b.NodeType));
                }

                this.Visit(b.Right);
            }

            sqlSB.Append(")");
            return b;
        }


        protected override Expression VisitConstant(ConstantExpression c)
        {
            if (c.Value == null)
            {
                sqlSB.Append("NULL");
            }
            else
            {
                switch (Type.GetTypeCode(c.Value.GetType()))
                {
                    case TypeCode.Boolean:
                        sqlSB.Append(((bool)c.Value) ? 1 : 0);
                        break;
                    case TypeCode.String:
                        sqlSB.Append("'");
                        sqlSB.Append(c.Value);
                        sqlSB.Append("'");
                        break;
                    case TypeCode.DateTime:
                        sqlSB.Append(string.Format("#{0:HH:mm:ss yyyy/MM/dd}#", c.Value));
                        break;
                    case TypeCode.Object:
                        throw new NotSupportedException(string.Format("The constant for '{0}' is not supported", c.Value));
                    default:
                        sqlSB.Append(c.Value);
                        break;
                }
            }

            return c;
        }


        protected override Expression VisitMember(MemberExpression m)
        {
            if (m.Expression != null && m.Expression.NodeType == ExpressionType.Parameter)
            {
                sqlSB.Append(m.Member.Name);
                return m;
            }

            throw new NotSupportedException(string.Format("The member '{0}' is not supported", m.Member.Name));
        }
    }
}
